import { Body, Controller, Delete, Get, Post, Query, UseGuards } from '@nestjs/common'
import { Throttle, minutes, seconds } from '@nestjs/throttler'
import { UAParser } from 'ua-parser-js'
import lodash from 'lodash'
import { EmailService } from '@app/processors/helper/helper.service.email'
import { IPLocation, IPService } from '@app/processors/helper/helper.service.ip'
import { CommentService } from '@app/modules/comment/comment.service'
import { ArticleService } from '@app/modules/article/article.service'
import { OptionService } from '@app/modules/option/option.service'
import { Author } from '@app/modules/comment/comment.model'
import { GUESTBOOK_POST_ID } from '@app/constants/biz.constant'
import * as APP_CONFIG from '@app/app.config'
import { Responser } from '@app/decorators/responser.decorator'
import { ExposePipe } from '@app/pipes/expose.pipe'
import { PaginateOptions, PaginateQuery, PaginateResult } from '@app/utils/paginate'
import { VoteService } from './vote.service'
import { Vote, VoteAuthorType, VoteTarget, voteTypeMap } from './vote.model'
import { CommentVoteDTO, PostVoteDTO, VotePaginateQueryDTO, VotesDTO } from './vote.dto'
import { QueryParams, QueryParamsResult } from '@app/decorators/queryparams.decorator'
import { getPermalinkByID } from '@app/transformers/urlmap.transformer'
import { AuthUserGuard } from '@app/guards/auth-user.guard'

@Controller('vote')
export class VoteController {
  constructor(
    private readonly ipService: IPService,
    private readonly emailService: EmailService,
    private readonly commentService: CommentService,
    private readonly articleService: ArticleService,
    private readonly optionService: OptionService,
    private readonly voteService: VoteService
  ) { }

  private async queryIPLocation(ip: string | null) {
    return ip ? await this.ipService.queryLocation(ip) : null
  }

  private async getPostTitle(postId: number) {
    if (postId === GUESTBOOK_POST_ID) {
      return 'guestbook'
    } else {
      const article = await this.articleService.getDetailByNumberIDOrSlug({ idOrSlug: postId })
      return article.toObject().title
    }
  }

  private async getVoteAuthor(payload: { guestAuthor?: Author; disqusToken?: string }) {
    const { guestAuthor, disqusToken } = payload ?? {}
    // Disqus user
    if (disqusToken) {
    }

    // local guest user
    if (guestAuthor) {
      return {
        type: VoteAuthorType.Guest,
        data: guestAuthor,
      }
    }

    // anonymous user
    return {
      type: VoteAuthorType.Anonymous,
      data: null,
    }
  }

  private getAuthorString(voteAuthor: { type: VoteAuthorType; data: any }) {
    // Disqus user
    // if (voteAuthor.type === VoteAuthorType.Disqus) {
    //   const disqusUser = voteAuthor.data
    //   const isAdmin = disqusUser.username === APP_CONFIG.DISQUS.adminUsername
    //   const userType = `Disqus ${isAdmin ? `moderator` : 'user'}`
    //   return [`${disqusUser.name} (${userType})`, disqusUser.profileUrl].filter(Boolean).join(' · ')
    // }

    // local guest user
    if (voteAuthor.type === VoteAuthorType.Guest) {
      const guestUser = voteAuthor.data
      return [`${guestUser.name} (Guest user)`, guestUser.site].filter(Boolean).join(' · ')
    }

    // anonymous user
    return `Anonymous user`
  }

  private emailToTargetVoteMessage(payload: {
    subject: string
    to: string
    on: string
    link: string
    vote: string
    author: string
    userAgent?: string
    location?: IPLocation | null
  }) {
    const getLocationText = (location: IPLocation) => {
      return [location.country, location.region, location.city].join(' · ')
    }

    const getAgentText = (ua: string) => {
      const uaResult = new UAParser(ua).getResult()
      return [
        `${uaResult.browser.name ?? 'unknown_browser'}@${uaResult.browser.version ?? 'unknown'}`,
        `${uaResult.os.name ?? 'unknown_OS'}@${uaResult.os.version ?? 'unknown'}`,
        `${uaResult.device.model ?? 'unknown_device'}@${uaResult.device.vendor ?? 'unknown'}`,
      ].join(' · ')
    }

    const mailTexts = [
      `${payload.subject} 在 "${payload.on}".`,
      `点赞: ${payload.vote}`,
      `评论人: ${payload.author}`,
      `位置: ${payload.location ? getLocationText(payload.location) : 'unknown'}`,
      `用户代理: ${payload.userAgent ? getAgentText(payload.userAgent) : 'unknown'}`,
    ]
    const textHTML = mailTexts.map((text) => `<p>${text}</p>`).join('')
    const linkHTML = `<a href="${payload.link}" target="_blank">${payload.on}</a>`

    this.emailService.sendMailAs(APP_CONFIG.APP.FE_NAME, {
      to: payload.to,
      subject: payload.subject,
      text: mailTexts.join('\n'),
      html: [textHTML, `<br>`, linkHTML].join('\n'),
    })
  }

  @Get()
  @UseGuards(AuthUserGuard)
  @Responser.handle('Get votes')
  getVotes(@Query(ExposePipe) query: VotePaginateQueryDTO): Promise<PaginateResult<Vote>> {
    const { sort, page, per_page, ...filters } = query
    const paginateQuery: PaginateQuery<Vote> = {}
    const paginateOptions: PaginateOptions = { page, perPage: per_page, dateSort: sort }
    // target type
    if (!lodash.isUndefined(filters.target_type)) {
      paginateQuery.target_type = filters.target_type
    }

    // target ID
    if (!lodash.isUndefined(filters.target_id)) {
      paginateQuery.target_id = filters.target_id
    }

    // vote type
    if (!lodash.isUndefined(filters.vote_type)) {
      paginateQuery.vote_type = filters.vote_type
    }

    // autor type
    if (!lodash.isUndefined(filters.author_type)) {
      paginateQuery.author_type = filters.author_type
    }

    return this.voteService.paginator(paginateQuery, paginateOptions)
  }

  @Delete()
  @UseGuards(AuthUserGuard)
  @Responser.handle('Delete votes')
  deleteVotes(@Body() body: VotesDTO) {
    return this.voteService.batchDelete(body.vote_ids)
  }

  // 1minute > limit 10
  @Post('/post')
  @Throttle({ default: { ttl: minutes(1), limit: 10 } })
  @Responser.handle('Vote post')
  async votePost(
    @Body() voteBody: PostVoteDTO,
    // @DisQusToken() token: AccessToken | null,
    @QueryParams() { visitor }: QueryParamsResult
  ) {
    // NodePress
    const likes =
      voteBody.post_id === GUESTBOOK_POST_ID
        ? await this.optionService.incrementLikes()
        : await this.articleService.incrementLikes(voteBody.post_id)
    // Disqus
    // ...
    // author
    this.getVoteAuthor({ guestAuthor: voteBody.author }).then(async (voteAuthor) => {
      // location
      const ipLocation = await this.queryIPLocation(visitor.ip)

      // database
      await this.voteService.create({
        target_type: VoteTarget.Post,
        target_id: voteBody.post_id,
        vote_type: voteBody.vote,
        author_type: voteAuthor.type,
        author: voteAuthor.data,
        user_agent: visitor.ua,
        ip: visitor.ip,
        ip_location: ipLocation,
      })
      // email to admin
      this.emailToTargetVoteMessage({
        to: APP_CONFIG.APP.ADMIN_EMAIL,
        subject: `你有一条新文章点赞`,
        on: await this.getPostTitle(voteBody.post_id),
        vote: voteTypeMap.get(voteBody.vote)!,
        author: this.getAuthorString(voteAuthor),
        userAgent: visitor.ua,
        location: ipLocation,
        link: getPermalinkByID(voteBody.post_id),
      })
    })

    return likes
  }

  // 30 seconds > limit 10
  @Post('/comment')
  @Throttle({ default: { ttl: seconds(30), limit: 10 } })
  @Responser.handle('Vote comment')
  async voteComment(
    @Body() voteBody: CommentVoteDTO,
    // @DisqusToken() token: AccessToken | null
    @QueryParams() { visitor }: QueryParamsResult
  ) {
    // NodePress
    const result = await this.commentService.vote(voteBody.comment_id, voteBody.vote > 0)
    // // Disqus only logged-in user
    // if (token) {
    //   try {
    //     const postID = await this.disqusPublicService.getDisqusPostIDByCommentID(voteBody.comment_id)
    //     if (postID) {
    //       await this.disqusPublicService.votePost({
    //         access_token: token.access_token,
    //         post: postID,
    //         vote: voteBody.vote,
    //       })
    //       // console.info(`Disqus like post ${voteBody.comment_id}`, result)
    //     }
    //   } catch (error) {}
    // }
    // effects
    this.getVoteAuthor({ guestAuthor: voteBody.author }).then(async (voteAuthor) => {
      // location
      const ipLocation = await this.queryIPLocation(visitor.ip)
      // database
      await this.voteService.create({
        target_type: VoteTarget.Comment,
        target_id: voteBody.comment_id,
        vote_type: voteBody.vote,
        author_type: voteAuthor.type,
        author: voteAuthor.data,
        user_agent: visitor.ua,
        ip: visitor.ip,
        ip_location: ipLocation,
      })
      const comment = await this.commentService.getDetailByNumberID(voteBody.comment_id)
      const targetTitle = await this.getPostTitle(comment.post_id)
      // email to author and admin
      const mailPayload = {
        vote: voteTypeMap.get(voteBody.vote)!,
        on: `${targetTitle} #${comment.id}`,
        author: this.getAuthorString(voteAuthor),
        userAgent: visitor.ua,
        location: ipLocation,
        link: getPermalinkByID(comment.post_id),
      }
      // email to admin
      this.emailToTargetVoteMessage({
        to: APP_CONFIG.APP.ADMIN_EMAIL,
        subject: `你有一条新评论点赞`,
        ...mailPayload,
      })
      // email to author
      if (comment.author.email) {
        this.emailToTargetVoteMessage({
          to: comment.author.email,
          subject: `你的评论 #${comment.id} 有一条新的点赞`,
          ...mailPayload,
        })
      }
    })

    return result
  }
}
